home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 #2 / Ham Radio 2000 - Volume 2.iso / HAMV2 / TCP_IP / TNOS230S / TCPOUT.C < prev    next >
Encoding:
C/C++ Source or Header  |  1996-08-29  |  6.8 KB  |  223 lines

  1. /* TCP output segment processing
  2.  * Copyright 1991 Phil Karn, KA9Q
  3.  */
  4. #include "global.h"
  5. #include "timer.h"
  6. #include "mbuf.h"
  7. #include "netuser.h"
  8. #include "iface.h"
  9.  
  10. #if !defined(_lint)
  11. static char rcsid[] OPTIONAL = "$Id: tcpout.c,v 1.7 1996/08/29 12:11:16 root Exp root $";
  12. #endif
  13.  
  14. long Tcp_maxwait;
  15.  
  16.  
  17. /* Send a segment on the specified connection. One gets sent only
  18.  * if there is data to be sent or if "force" is non zero
  19.  */
  20. void
  21. tcp_output (tcb)
  22. register struct tcb *tcb;
  23. {
  24. struct pseudo_header ph;/* Pseudo-header for checksum calcs */
  25. struct mbuf *hbp, *dbp;    /* Header and data buffer pointers */
  26. int16 hsize;        /* Size of header */
  27. struct tcp seg;        /* Local working copy of header */
  28. int16 ssize;        /* Size of current segment being sent,
  29.              * including SYN and FIN flags */
  30. int16 dsize;        /* Size of segment less SYN and FIN */
  31. int16 usable;        /* Usable window */
  32. int16 sent;        /* Sequence count (incl SYN/FIN) already
  33.              * in the pipe but not yet acked */
  34. int32 rto;        /* Retransmit timeout setting */
  35.  
  36.     if (tcb == NULLTCB)
  37.         return;
  38.  
  39.     switch (tcb->state) {
  40.         case TCP_LISTEN:
  41.         case TCP_CLOSED:
  42.             return;        /* Don't send anything */
  43.         default:
  44.             break;
  45.     }
  46.     for (;;) {
  47.         /* Compute data already in flight */
  48.         sent = (int16) (tcb->snd.ptr - tcb->snd.una);
  49.  
  50.         /* If transmitter has been idle for more than a RTT,
  51.          * take the congestion window back down to one packet.
  52.          */
  53.         if (!run_timer (&tcb->timer) && (msclock () - tcb->lastactive) > tcb->srtt)
  54.             tcb->cwind = tcb->mss;
  55.  
  56.         /* Compute usable send window as minimum of offered
  57.          * and congestion windows, minus data already in flight.
  58.          * Be careful that the window hasn't shrunk --
  59.          * these are unsigned vars.
  60.          */
  61.         usable = min (tcb->snd.wnd, tcb->cwind);
  62.         if (usable > sent)
  63.             usable -= sent;    /* Most common case */
  64.         else if (usable == 0 && sent == 0)
  65.             usable = 1;    /* Closed window probe */
  66.         else
  67.             usable = 0;    /* Window closed or shrunken */
  68.  
  69.         /* Compute size of segment we *could* send. This is the
  70.          * smallest of the usable window, the mss, or the amount
  71.          * we have on hand. (I don't like optimistic windows)
  72.          */
  73.         ssize = min (tcb->sndcnt - sent, usable);
  74.         ssize = min (ssize, tcb->mss);
  75.  
  76.         /* Now we decide if we actually want to send it.
  77.          * Apply John Nagle's "single outstanding segment" rule.
  78.          * If data is already in the pipeline, don't send
  79.          * more unless it is MSS-sized or the very last packet.
  80.          */
  81.         if (sent != 0 && ssize < tcb->mss && !(tcb->state == TCP_FINWAIT1 && ssize == tcb->sndcnt - sent))
  82.             ssize = 0;
  83.  
  84.         /* Unless the tcp syndata option is on, inhibit data until
  85.          * our SYN has been acked. This ought to be OK, but some
  86.          * old TCPs have problems with data piggybacked on SYNs.
  87.          */
  88.         if (!tcb->flags.synack && !tcb->parms->syndata) {
  89.             if (tcb->snd.ptr == tcb->iss)
  90.                 ssize = min (1, ssize);    /* Send only SYN */
  91.             else
  92.                 ssize = 0;    /* Don't send anything */
  93.         }
  94.         if (ssize == 0 && !tcb->flags.force)
  95.             break;    /* No need to send anything */
  96.  
  97.         tcb->flags.force = 0;    /* Only one forced segment! */
  98.  
  99.         seg.source = tcb->conn.local.port;
  100.         seg.dest = tcb->conn.remote.port;
  101.  
  102.         /* Set the flags according to the state we're in. It is
  103.          * assumed that if this segment is associated with a state
  104.          * transition, then the state change will already have been
  105.          * made. This allows this routine to be called from a
  106.          * retransmission timeout with force=1.
  107.          */
  108.         seg.flags.urg = 0;    /* Not used in this implementation */
  109.         seg.flags.rst = 0;
  110.         seg.flags.ack = 1;    /* Every state except TCP_SYN_SENT */
  111.         seg.flags.syn = 0;    /* syn/fin/psh set later if needed */
  112.         seg.flags.fin = 0;
  113.         seg.flags.psh = 0;
  114.         seg.flags.congest = tcb->flags.congest;
  115.  
  116.         hsize = TCPLEN;    /* Except when SYN being sent */
  117.         seg.mss = 0;
  118.         seg.optlen = 0;
  119.  
  120.         if (tcb->state == TCP_SYN_SENT)
  121.             seg.flags.ack = 0;    /* Haven't seen anything yet */
  122.  
  123.         dsize = ssize;
  124.         if (!tcb->flags.synack && tcb->snd.ptr == tcb->iss) {
  125.             /* Send SYN */
  126.             seg.flags.syn = 1;
  127.             dsize--;/* SYN isn't really in snd queue */
  128.             /* Also send MSS */
  129.             seg.mss = tcb->parms->mss;
  130.             seg.optlen = 0;
  131.             hsize = TCPLEN + MSS_LENGTH;
  132.         }
  133.         seg.seq = tcb->snd.ptr;
  134.         seg.ack = tcb->rcv.nxt;
  135.         seg.wnd = tcb->rcv.wnd;
  136.         seg.up = 0;
  137.  
  138.         /* Now try to extract some data from the send queue. Since
  139.          * SYN and FIN occupy sequence space and are reflected in
  140.          * sndcnt but don't actually sit in the send queue, dup_p
  141.          * will return one less than dsize if a FIN needs to be sent.
  142.          */
  143.         if (dsize != 0) {
  144.             int16 offset;
  145.  
  146.             /* SYN doesn't actually take up space on the sndq,
  147.              * so take it out of the sent count
  148.              */
  149.             offset = sent;
  150.             if (!tcb->flags.synack && sent != 0)
  151.                 offset--;
  152.  
  153.             if (dup_p (&dbp, tcb->sndq, offset, dsize) != dsize) {
  154.                 /* We ran past the end of the send queue;
  155.                  * send a FIN
  156.                  */
  157.                 seg.flags.fin = 1;
  158.                 dsize--;
  159.             }
  160.         } else
  161.             dbp = NULLBUF;
  162.  
  163.         /* If the entire send queue will now be in the pipe, set the
  164.          * push flag
  165.          */
  166.         if (dsize != 0 && sent + ssize == tcb->sndcnt)
  167.             seg.flags.psh = 1;
  168.  
  169.         /* If this transmission includes previously transmitted data,
  170.          * snd.nxt will already be past snd.ptr. In this case,
  171.          * compute the amount of retransmitted data and keep score
  172.          */
  173.         if (tcb->snd.ptr < tcb->snd.nxt)
  174.             tcb->resent += min (tcb->snd.nxt - tcb->snd.ptr, ssize);
  175.  
  176.         tcb->snd.ptr += ssize;
  177.         /* If this is the first transmission of a range of sequence
  178.          * numbers, record it so we'll accept acknowledgments
  179.          * for it later
  180.          */
  181.         if (seq_gt (tcb->snd.ptr, tcb->snd.nxt))
  182.             tcb->snd.nxt = tcb->snd.ptr;
  183.  
  184.         /* Fill in fields of pseudo IP header */
  185.         ph.source = tcb->conn.local.address;
  186.         ph.dest = tcb->conn.remote.address;
  187.         ph.protocol = TCP_PTCL;
  188.         ph.length = hsize + dsize;
  189.  
  190.         /* Generate TCP header, compute checksum, and link in data */
  191.         if ((hbp = htontcp (&seg, dbp, &ph)) == NULLBUF) {
  192.             free_p (dbp);
  193.             return;
  194.         }
  195.         /* If we're sending some data or flags, start retransmission
  196.          * and round trip timers if they aren't already running.
  197.          */
  198.         if (ssize != 0) {
  199.             /* Set round trip timer. */
  200.             rto = backoff (tcb) * (4 * tcb->mdev + tcb->srtt);
  201.             if (tcb->parms->maxwait && rto > tcb->parms->maxwait)
  202.                 rto = tcb->parms->maxwait;
  203.             set_timer (&tcb->timer, max (MIN_RTO, rto));
  204.             if (!run_timer (&tcb->timer))
  205.                 start_timer (&tcb->timer);
  206.  
  207.             /* If round trip timer isn't running, start it */
  208.             if (!tcb->flags.rtt_run) {
  209.                 tcb->flags.rtt_run = 1;
  210.                 tcb->rtt_time = msclock ();
  211.                 tcb->rttseq = tcb->snd.ptr;
  212.             }
  213.         }
  214.         if (tcb->flags.retran)
  215.             tcpRetransSegs++;
  216.         else
  217.             tcpOutSegs++;
  218.  
  219.         (void) ip_send (tcb->conn.local.address, tcb->conn.remote.address,
  220.                 TCP_PTCL, tcb->tos, 0, hbp, ph.length, 0, 0);
  221.     }
  222. }
  223.